package com.wonders.framework.dao.impl;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.wonders.framework.dao.BaseDao;
import com.wonders.framework.dao.transformer.BeanTransformer;
import com.wonders.framework.dao.transformer.MapTransformer;
import com.wonders.framework.entity.bo.BaseBo;
import com.wonders.framework.entity.vo.Page;
import com.wonders.framework.entity.vo.Parameter;
import com.wonders.framework.exception.FrameworkException;
import com.wonders.framework.util.dao.FormatUtil;
import com.wonders.framework.util.dao.ParamsUtil;

/**
 * 基础数据操作方法实现
 * 
 * @author WuBin
 * 
 */
@Repository
public class BaseDaoImpl implements BaseDao {

	@Autowired
	private SessionFactory sessionFactory;

	/**
	 * 获取当前session
	 * 
	 * @return session
	 */
	public Session getCurrentSession() {
		return sessionFactory.getCurrentSession();
	}

	public void save(Object object) {
		this.getCurrentSession().save(object);
	}

	public void update(Object object) {
		this.getCurrentSession().update(object);
	}

	public void saveOrUpdate(Object object) {
		this.getCurrentSession().saveOrUpdate(object);
	}

	public void remove(Object object) {
		this.getCurrentSession().delete(object);
	}

	public void remove(Class clazz, Serializable id) {
		this.remove(this.load(clazz, id));
	}

	public <T extends Object> T get(Class<T> clazz, Serializable id) {
		return (T) this.getCurrentSession().get(clazz, id);
	}

	public <T extends Object> T load(Class<T> clazz, Serializable id) {
		return (T) this.getCurrentSession().load(clazz, id);
	}

	public Long getSequence(String name) {
		String sql = StringUtils.join("select ", name, ".nextval from dual");
		SQLQuery query = this.getCurrentSession().createSQLQuery(sql);
		return ((BigDecimal) query.uniqueResult()).longValue();
	}

	public boolean excuteHql(String hql) {
		return this.excuteHql(hql, null);
	}

	public boolean excuteHql(String hql, List<Parameter> args) {
		Query query = this.getCurrentSession().createQuery(hql);
		ParamsUtil.setQueryParameters(query, args);
		if (query.executeUpdate() > 0) {
			return true;
		}
		return false;
	}

	public Integer countByHql(String hql) {
		return this.countByHql(hql, null);
	}

	public Integer countByHql(String hql, List<Parameter> args) {
		int index = hql.indexOf("from");
		if (index == -1) {
			throw new FrameworkException("hql语句有误");
		}
		hql = StringUtils.join("select count(*)", hql.substring(index));
		Query query = this.getCurrentSession().createQuery(hql);
		ParamsUtil.setQueryParameters(query, args);
		return ((Long) query.uniqueResult()).intValue();
	}

	public Integer countByCriteria(Criteria criteria) {
		criteria.setProjection(Projections.rowCount());
		Long count = (Long) criteria.uniqueResult();
		criteria.setProjection(null);
		return count.intValue();
	}

	public <T extends Object> List<T> listByHql(String hql, Class<T> clazz) {
		return this.listByHql(hql, null, clazz);
	}

	public <T extends Object> List<T> listByHql(String hql,
			List<Parameter> args, Class<T> clazz) {
		Query query = this.getCurrentSession().createQuery(hql);
		ParamsUtil.setQueryParameters(query, args);
		return query.list();
	}

	public <T extends Object> List<T> listByCriteria(Criteria criteria,
			Class<T> clazz) {
		return criteria.list();
	}

	public Page pageByHql(String hql, Page page) {
		return this.pageByHql(hql, null, page);
	}

	public Page pageByHql(String hql, List<Parameter> args, Page page) {
		page.setTotalSize(this.countByHql(hql.replace("#order", ""), args));
		page.setTotalPage((int) ((page.getTotalSize() - 1) / page.getPageSize()) + 1);
		if (StringUtils.isNotBlank(page.getSortField())) {
			if ("asc".equals(page.getSortOrder())) {
				hql = hql.replace("#order", "order by " + page.getSortField()
						+ " asc");
			} else {
				hql = hql.replace("#order", "order by " + page.getSortField()
						+ " desc");
			}
		} else {
			hql = hql.replace("#order", "");
		}
		Query query = this.getCurrentSession().createQuery(hql);
		ParamsUtil.setQueryParameters(query, args);
		query.setFirstResult((page.getPageNum() - 1) * page.getPageSize());
		query.setMaxResults(page.getPageSize());
		List data = query.list();
		if (null != data) {
			page.setData(data);
		}
		return page;
	}

	public Page pageByCriteria(Criteria criteria, Page page) {
		page.setTotalSize(this.countByCriteria(criteria));
		page.setTotalPage((int) ((page.getTotalSize() - 1) / page.getPageSize()) + 1);
		criteria.setFirstResult((page.getPageNum() - 1) * page.getPageSize());
		criteria.setMaxResults(page.getPageSize());
		if (StringUtils.isNotBlank(page.getSortField())) {
			if ("asc".equals(page.getSortOrder())) {
				criteria.addOrder(Order.asc(page.getSortField()));
			} else {
				criteria.addOrder(Order.desc(page.getSortField()));
			}
		}
		page.setData(criteria.list());
		return page;
	}

	public <T extends Object> T findByHql(String hql, Class<T> clazz) {
		return this.findByHql(hql, null, clazz);
	}

	public <T extends Object> T findByHql(String hql, List<Parameter> args,
			Class<T> clazz) {
		Query query = this.getCurrentSession().createQuery(hql);
		ParamsUtil.setQueryParameters(query, args);
		List list = query.list();
		if (null == list || list.size() == 0) {
			return null;
		} else if (list.size() > 1) {
			throw new FrameworkException("查询到多个符合条件的结果");
		} else {
			return (T) list.get(0);
		}
	}

	public <T extends Object> T findByCriteria(Criteria criteria, Class<T> clazz) {
		List list = criteria.list();
		if (null == list || list.size() == 0) {
			return null;
		} else if (list.size() > 1) {
			throw new FrameworkException("查询到多个符合条件的结果");
		} else {
			return (T) list.get(0);
		}
	}

	public boolean excuteSql(String sql) {
		return this.excuteSql(sql, null);
	}

	public boolean excuteSql(String sql, List<Parameter> args) {
		SQLQuery query = this.getCurrentSession().createSQLQuery(sql);
		ParamsUtil.setQueryParameters(query, args);
		if (query.executeUpdate() > 0) {
			return true;
		}
		return false;
	}

	public Integer countBySql(String sql) {
		return this.countBySql(sql, null);
	}

	public Integer countBySql(String sql, List<Parameter> args) {
		sql = StringUtils.join("select count(1) from (", sql, ")");
		SQLQuery query = this.getCurrentSession().createSQLQuery(sql);
		ParamsUtil.setQueryParameters(query, args);
		return ((BigDecimal) query.uniqueResult()).intValue();
	}

	public List<Map<String, Object>> listBySql(String sql) {
		return this.listBySql(sql, null, null);
	}

	public List<Map<String, Object>> listBySql(String sql, List<Parameter> args) {
		return this.listBySql(sql, args, null);
	}

	public <T extends Object> List<T> listBySql(String sql, Class<T> clazz) {
		return this.listBySql(sql, null, clazz);
	}

	public <T extends Object> List<T> listBySql(String sql,
			List<Parameter> args, Class<T> clazz) {
		SQLQuery query = this.getCurrentSession().createSQLQuery(sql);
		ParamsUtil.setQueryParameters(query, args);
		if (null == clazz) {
			query.setResultTransformer(new MapTransformer());
		} else {
			if (BaseBo.class.isAssignableFrom(clazz)) {
				query.addEntity(clazz);
			} else {
				query.setResultTransformer(new BeanTransformer(clazz));
			}
		}
		return query.list();
	}

	public Page pageBySql(String sql, Page page) {
		return this.pageBySql(sql, null, null, page);
	}

	public Page pageBySql(String sql, List<Parameter> args, Page page) {
		return this.pageBySql(sql, args, null, page);
	}

	public Page pageBySql(String sql, Class clazz, Page page) {
		return this.pageBySql(sql, null, clazz, page);
	}

	public Page pageBySql(String sql, List<Parameter> args, Class clazz,
			Page page) {
		page.setTotalSize(this.countBySql(sql, args));
		page.setTotalPage((int) ((page.getTotalSize() - 1) / page.getPageSize()) + 1);
		if (StringUtils.isNotBlank(page.getSortField())) {
			sql = "select * from (" + sql + ") order by "
					+ FormatUtil.parseField(page.getSortField());
			if ("asc".equals(page.getSortOrder())) {
				sql += " asc";
			} else {
				sql += " desc";
			}
		}
		SQLQuery query = this.getCurrentSession().createSQLQuery(sql);
		ParamsUtil.setQueryParameters(query, args);
		if (null == clazz) {
			query.setResultTransformer(new MapTransformer());
		} else {
			if (BaseBo.class.isAssignableFrom(clazz)) {
				query.addEntity(clazz);
			} else {
				query.setResultTransformer(new BeanTransformer(clazz));
			}
		}
		query.setFirstResult((page.getPageNum() - 1) * page.getPageSize());
		query.setMaxResults(page.getPageSize());
		List data = query.list();
		if (null != data) {
			page.setData(data);
		}
		return page;
	}

	public Map<String, Object> findBySql(String sql) {
		return (Map<String, Object>) this.findBySql(sql, null, null);
	}

	public Map<String, Object> findBySql(String sql, List<Parameter> args) {
		return (Map<String, Object>) this.findBySql(sql, args, null);
	}

	public <T extends Object> T findBySql(String sql, Class<T> clazz) {
		return this.findBySql(sql, null, clazz);
	}

	public <T extends Object> T findBySql(String sql, List<Parameter> args,
			Class<T> clazz) {
		SQLQuery query = this.getCurrentSession().createSQLQuery(sql);
		ParamsUtil.setQueryParameters(query, args);
		if (null == clazz) {
			query.setResultTransformer(new MapTransformer());
		} else {
			if (BaseBo.class.isAssignableFrom(clazz)) {
				query.addEntity(clazz);
			} else {
				query.setResultTransformer(new BeanTransformer(clazz));
			}
		}
		List list = query.list();
		if (null == list || list.size() == 0) {
			return null;
		} else if (list.size() > 1) {
			throw new FrameworkException("查询到多个符合条件的结果");
		} else {
			return (T) list.get(0);
		}
	}

}
